package Principal;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import entidades.Aeropuerto;
import entidades.Estado;
import entidades.IFlightController;
import entidades.Reserva;
import entidades.Vuelo;

public class ConsoleView implements View {

	protected String readLine(String option) {
		System.out.println(option);
        try{
        	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            return br.readLine();	
        }catch(Exception e){
        	return this.readLine(option);
        }
	}

	protected void printLine(String text) {
		System.out.println(text);
	}

	public void showOperationsMenu() {
		this.printLine("--Opciones--");
		this.printLine("Por favor seleccione alguna de las opciones a continuacion:");
		this.printLine("-----------Aeropuertos-----------------------------------------");
		this.printLine("[" + Const.AIRPORT_ADD + "] Agregar Aeropuerto");
		this.printLine("[" + Const.AIRPORT_LIST + "] Listar Aeropuertos");
		this.printLine("[" + Const.AIRPORT_DELETE + "] Borrar Aeropuerto");
		this.printLine("[" + Const.AIRPORT_DUMMY_ADD + "] Agregar aeropuerto Dummy");

		this.printLine("-----------Vuelos----------------------------------------------");
		this.printLine("[" + Const.FLIGHT_ADD + "] Agregar Vuelo");
		this.printLine("[" + Const.FLIGHT_LIST + "] Listar Vuelos");
		this.printLine("[" + Const.FLIGHT_DELETE + "] Borrar Vuelo");
		this.printLine("[" + Const.FLIGHT_CHECK_STATUS + "] Checkear estado Vuelo");
		this.printLine("[" + Const.FLIGHT_SET_STATUS + "] Modificar estado de un vuelo");
		this.printLine("[" + Const.FLIGHT_DUMMY_ADD + "] Agregar vuelo Dummy");

		this.printLine("-----------Reservas--------------------------------------------");
		this.printLine("[" + Const.RESERVATIONS_ADD + "] Agregar Reserva");
		this.printLine("[" + Const.RESERVATIONS_LIST + "] Listar Reservas");
		this.printLine("[" + Const.RESERVATIONS_CANCEL + "] Cancelar Reservas");

		this.printLine("---------------------------------------------------------------");
		this.printLine("[" + Const.EXIT + "] Salir");
	}

	public void showFlightList(List<Vuelo> flights) {
		this.showFlightsMenu(flights);
	}

	public void showErrorMessage(String message) {
		this.printLine("ERROR: " + message);
	}

	public int getSelectedOperation() {
		this.showOperationsMenu();
		return this.getNumericChoice();
	}

	protected int getNumericChoice() {
		String choice = this.readLine("-");
		try {
			return Integer.parseInt(choice);
		} catch (NumberFormatException e) {
			this.printLine(Const.MSG_ERROR_NOT_NUM);
			return this.getNumericChoice();
		}
	}

	public Aeropuerto getSelectedAirport(List<Aeropuerto> airports) {
		this.showAirportsMenu(airports);
		int option = this.getNumericChoice();
		try {
			return airports.get(option);
		} catch (Exception e) {
			this.printLine("El numero de aeropuerto ingresado no existe. Por favor elija otro");
			return this.getSelectedAirport(airports);
		}

	}

	public List<Vuelo> getSelectedCombination(HashMap<String, List<Vuelo>> options) {
		this.showCombinationMenu(options);
		int option = this.getNumericChoice();
		try {
			return (option == 0) ? options.get("precio") : options.get("tiempo");
		} catch (Exception e) {
			this.printLine("El numero de opcion ingresado no es valido. Por favor elija otro");
			return this.getSelectedCombination(options);
		}
	}

	public Vuelo getFlight(Vuelo flight, List<Aeropuerto> airports) {
		flight.setNroVuelo(this.readLine("Ingrese el Numero del vuelo:"));

		this.printLine("Seleccione el aeropuerto de origen");
		flight.setOrigen(this.getSelectedAirport(airports));

		while (!flight.validateOrigen()) {
			this.printLine(flight.MSG_VALIDATE_ORIGEN);
			flight.setDestino(this.getSelectedAirport(airports));
		}

		this.printLine("Seleccione el aeropuerto de destino");
		flight.setDestino(this.getSelectedAirport(airports));

		while (!flight.validateDestino()) {
			this.printLine(flight.MSG_VALIDATE_DESTINO);
			flight.setDestino(this.getSelectedAirport(airports));
		}

		this.printLine("Ingrese la cantidad de asientos disponibles");
		flight.setCantDisponible(this.getNumericChoice());

		while (!flight.validateCantDisponible()) {
			this.printLine(flight.MSG_VALIDATE_CANT_DISPONIBLE);
			flight.setCantDisponible(this.getNumericChoice());
		}

		this.printLine("Ingrese el precio que va a tener este vuelo");

		flight.setPrecio((double) this.getNumericChoice());
		while (!flight.validatePrecio()) {
			this.printLine(flight.MSG_VALIDATE_PRECIO);
			flight.setPrecio((double) this.getNumericChoice());
		}

		this.printLine("Ingrese a continuacion la fecha de salida");
		flight.setSalida(this.getDate());

		this.printLine("Ingrese a continuacion la fecha de llegada");
		flight.setLlegada(this.getDate());

		return flight;

	}

	// @Override
	// public Vuelo getSelectedFlight(List<Vuelo> flights) {
	//
	// }

	public Date getDate() {
		String pattern = "yyyy-MM-dd HH:mm";
		SimpleDateFormat format = new SimpleDateFormat(pattern);
		try {
			this.printLine(format.toPattern());
			return format.parse(this.readLine("-"));

		} catch (ParseException e) {
			this.printLine(e.getMessage() + e.getCause());
			this.printLine("El formato no es valido - " + format.toPattern());
			return this.getDate();
		}
	}

	public void getAirport(Aeropuerto airport) {
		this.printLine("Ingrese el nombre del Aeropuerto");
		airport.setName(this.readLine("-"));
		while (!airport.validateName()) {
			this.printLine(airport.getValidateNameParams());
			this.printLine("Ingrese el nombre del Aeropuerto");
			airport.setName(this.readLine("-"));
		}

	}

	public void showAirportList(List<Aeropuerto> airports) {
		this.printLine("--Aeropuertos--");
		this.showAirportsMenu(airports);
		this.printLine("total - " + airports.size());
	}

	public void showFlightsMenu(List<Vuelo> flights) {
		int i = 0;
		for (Vuelo flight : flights) {
			this.printLine("[" + i++ + "]" + flight.getNroVuelo());
			this.printLine("Estado: " + flight.getEstado());
			this.printLine("Asientos " + flight.getCantDisponible() + ", $" + flight.getPrecio());
			this.printLine("Desde " + flight.getOrigen().getName() + "(" + flight.getSalida().toString() + ")");
			this.printLine("Hasta " + flight.getDestino().getName() + "(" + flight.getLlegada().toString() + ")");
			this.printLine("Tiempo estimado de vuelo " + this.getFormatedTimeDiff(flight.getSalida(), flight.getLlegada()));
		}
	}

	public void showStatusMenu() {
		int i = 0;
		for (Estado status : Estado.values()) {
			this.printLine("[" + i++ + "]" + status.name());
		}
	}

	/**
	 * 
	 * @param airports
	 * @return number of airports printed
	 */

	protected void showAirportsMenu(List<Aeropuerto> airports) {
		int i = 0;
		for (Aeropuerto airport : airports) {
			this.printLine("[" + i++ + "]" + airport.getName());
		}
	}

	protected void showCombinationMenu(HashMap<String, List<Vuelo>> options) {
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
		String out = "";

		double precio = 0;
		long tiempo = 0;

		List<Vuelo> option = options.get("precio");
		out += "[0] Menor precio \n";

		for (int j = 0; j < option.size(); j++) {
			precio += option.get(j).getPrecio();

			tiempo += this.getTimeDiff(option.get(j).getLlegada(), option.get(j).getSalida());
			if (j > 0) {
				tiempo += this.getTimeDiff(option.get(j).getSalida(), option.get(j - 1).getLlegada());
			}

			out += "Salida: " + option.get(j).getOrigen().getName() + " " + sdf.format(option.get(j).getSalida()) + " - Llegada: " + option.get(j).getDestino().getName() + " " + sdf.format(option.get(j).getLlegada()) + "\n";
		}
		out += "Precio: $" + precio + " Tiempo total de viaje (incluyendo escalas): " + this.getFormatedTime(tiempo) + " \n\n";

		precio = 0;
		tiempo = 0;

		option = options.get("tiempo");
		out += "[1] Menor tiempo \n";

		for (int j = 0; j < option.size(); j++) {
			precio += option.get(j).getPrecio();

			tiempo += this.getTimeDiff(option.get(j).getLlegada(), option.get(j).getSalida());
			if (j > 0) {
				tiempo += this.getTimeDiff(option.get(j).getSalida() , option.get(j - 1).getLlegada());
			}

			out += "Salida: " + option.get(j).getOrigen().getName() + " " + sdf.format(option.get(j).getSalida()) + " - Llegada: " + option.get(j).getDestino().getName() + " " + sdf.format(option.get(j).getLlegada()) + " \n";
		}
		out += "Precio: $" + precio + " Tiempo total de viaje (incluyendo escalas): " + this.getFormatedTime(tiempo) + " minutos \n";

		this.printLine(out);

	}
	
	protected String getFormatedTime(long time){
		return String.format("%d hour(s) %d min(s)", TimeUnit.MILLISECONDS.toHours(time), TimeUnit.MILLISECONDS.toMinutes(time) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(time)));
	}

	protected long getTimeDiff(Date dateOne, Date dateTwo){
		return Math.abs(dateOne.getTime() - dateTwo.getTime());
	}
	
	protected String getFormatedTimeDiff(Date dateOne, Date dateTwo) {
		return this.getFormatedTime(this.getTimeDiff(dateOne, dateTwo));
	}

	@Override
	public Vuelo getSelectedFlight(List<Vuelo> flights) {
		this.showFlightsMenu(flights);
		this.printLine("Seleccione un vuelo");
		int option = this.getNumericChoice();
		try {
			return flights.get(option);
		} catch (Exception e) {
			this.printLine("El numero de vuelo ingresado no existe. Por favor elija otro");
			return this.getSelectedFlight(flights);
		}
	}

	@Override
	public Estado getSelectedStatus() {
		this.showStatusMenu();
		this.printLine("Seleccione un estado");
		int option = this.getNumericChoice();
		try {
			return Estado.values()[option];
		} catch (Exception e) {
			this.printLine("El numero de vuelo ingresado no existe. Por favor elija otro");
			return this.getSelectedStatus();
		}
	}

	@Override
	public void deleteAirport(Aeropuerto airport) {
		this.printLine("Se va a borrar el aeropuerto " + airport.getName());
	}

	@Override
	public void showAirportStatus(Aeropuerto selectedAirport, List<Vuelo> flights, Date date) {
		this.printLine("Aeropuerto: " + selectedAirport.getName());
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
		for (Vuelo flight : flights) {
			if (flight.getOrigen().equals(selectedAirport) && sdf.format(date).equals(sdf.format(flight.getSalida())) && flight.getEstado() != null) {
				if (flight.getEstado().equals(Estado.Anunciado)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.Canceladoyfinalizado)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.Demorado)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.Embarcando)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.EnVueloDespegando)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
			}

			if (flight.getDestino().equals(selectedAirport) && sdf.format(date).equals(sdf.format(flight.getLlegada())) && flight.getEstado() != null) {
				if (flight.getEstado().equals(Estado.Canceladoyfinalizado)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.EnVueloArribando)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
				if (flight.getEstado().equals(Estado.Aterrizando)) {
					this.printLine(flight.getNroVuelo() + " " + flight.getEstado());
				}
			}
		}
	}

	public Reserva getNewReservation(IFlightController controller) {
		try {
			Reserva reservation = new Reserva();

			this.printLine("Ingrese el nombre del cliente");
			reservation.setNombre(this.readLine("-"));
			
			while(!reservation.validateName()){
				this.printLine(reservation.getValidateNameParams());
				reservation.setNombre(this.readLine("-"));
					
			}

			this.printLine("Seleccione el aeropuerto de origen");
			Aeropuerto origen = this.getSelectedAirport(controller.getAirports());

			this.printLine("Seleccione el aeropuerto de destino");
			Aeropuerto destino = this.getSelectedAirport(controller.getAirports());

			while(origen.equals(destino)){
				this.printLine("Ingrese un aeropuerto diferente para el origen y el destino");
				destino = this.getSelectedAirport(controller.getAirports());
	
			}
			
			this.printLine("Ingrese la fecha en la que sea viajar");
			Date fecha = this.getDate();

			this.printLine("Ingrese la cantidad de pasajes");
			reservation.setCantidad(this.getNumericChoice());
			while(!reservation.validateCantidad()){
				this.printLine(reservation.getValidateCantidadParams());
				reservation.setCantidad(this.getNumericChoice());
			}

			this.printLine("Ingrese la cantidad maxima de horas que desea esperar");
			int espera = this.getNumericChoice();

			reservation.setVuelos(this.getSelectedCombination(controller.getRoutes(origen, destino, reservation.getCantidad(), espera, fecha)));

			return reservation;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		return null;
	}

	public void showReservationMenu(List<Reserva> reservations) {
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
		for (int i = 0; i < reservations.size(); i++) {
			double precio = 0;
			long tiempo = 0;

			this.printLine("");
			this.printLine("[" + i + "] " + reservations.get(i).getNombre() + "");

			for (int j = 0; j < reservations.get(i).getVuelos().size(); j++) {
				precio += reservations.get(i).getVuelos().get(j).getPrecio();

				tiempo += Math.abs(reservations.get(i).getVuelos().get(j).getLlegada().getTime() - reservations.get(i).getVuelos().get(j).getSalida().getTime());
				if (j > 0) {
					tiempo += Math.abs(reservations.get(i).getVuelos().get(j).getSalida().getTime() - reservations.get(i).getVuelos().get(j - 1).getLlegada().getTime());
				}

				this.printLine("Salida: " + reservations.get(i).getVuelos().get(j).getOrigen().getName() + " " + sdf.format(reservations.get(i).getVuelos().get(j).getSalida()) + " - Llegada: " + reservations.get(i).getVuelos().get(j).getDestino().getName() + " " + sdf.format(reservations.get(i).getVuelos().get(j).getLlegada()) + "");
			}
			this.printLine("Precio: $" + precio + " Tiempo total de viaje (incluyendo escalas): " + tiempo);
		}
	}

	@Override
	public void showReservationList(List<Reserva> reservations) {
		this.showReservationMenu(reservations);
	}

	@Override
	public Reserva getSelectedReservation(List<Reserva> reservations) {
		this.showReservationMenu(reservations);
		int option = this.getNumericChoice();
		try {
			return reservations.get(option);
		} catch (Exception e) {
			this.printLine("El numero de reserva ingresado no existe. Por favor elija otro");
			return this.getSelectedReservation(reservations);
		}
	}

}
